#include "app_file.h"
#include <stdio.h>
#include <string.h>
#include "ff.h"
#include "at32_sdio.h"

/* 变量 */
static FATFS fatfs;
static FIL   file;

static image_header_t image_info;	// boot文件信息
static up_file_info_t up_file_info[APP_FILE_CAPACITY];
static DWORD clmt[SZ_TBL];




/**
***********************************************************
* @brief	检查是否是.up文件 
* @param 	
* @return 	
***********************************************************
*/
static uint8_t is_up_file(const uint8_t *file_name)
{
	uint32_t i = 0;

	/* 短文件名缓存区长度=13 */
	for(i=0; i<10; i++)
	{
		if(file_name[i] == '.')
		{
			if(((file_name[i+1] == 'U') || (file_name[i+1] == 'u')) 
			&& ((file_name[i+2] == 'P') || (file_name[i+2] == 'p')))
			{
				return 0;
			}
		}
	}
	return 1;
}
/**
***********************************************************
* @brief	查找.up文件
* @param	文件信息指针 	
* @return 	状态
***********************************************************
*/

uint8_t app_file_find(app_file_t	 *app_file)
{
	FRESULT res;
	FILINFO fno;
	DIR dir;
	char fileName[12] = {'1',':'};
	
	uint8_t temp_file_count = 0;
	uint32_t numOfReadBytes = 0;
	up_file_info_t *temp_up_file_info;

	/* 挂载文件系统 */
	res = f_mount(&fatfs, "1:",1);
	if(res != FR_OK)
	{
		DBG_Error("fs mount err:%d",res);
		return res;
	}

	/* 打开根目录 */
	res = f_opendir(&dir, "1:");
	if(res != FR_OK)
	{
		DBG_Error("open dir err:%d",res);
		return res;
	}

	/* 检索目录下的文件 */
	while(1)
	{
		res = f_readdir(&dir, &fno);
		if(res != FR_OK)
			break;
		/* 目录遍历结束 */
		if(fno.fname[0] == 0)
			break;

		/* 超出可以读取的文件个数 */
		if(app_file->count >= APP_FILE_CAPACITY)
		{
			DBG_Warning("app file capacity %d",app_file->count);
			break;	
		}
		

		/* 排除隐藏文件 */
		if (fno.fname[0] == '.')
            continue;

		/* 排除文件夹路径 */
		if((fno.fattrib & AM_DIR) == AM_DIR)
			continue;

		/* 排除非需要文件 */
		if(!is_up_file((uint8_t *)fno.fname))
		{
			DBG_log("app file name:%s file size:%dKB",fno.fname, (fno.fsize / 1024));
			strcat(fileName,fno.fname);

			/* 读取文件信息 */
			temp_up_file_info = &up_file_info[app_file->count];
			memcpy(temp_up_file_info->file_name,fileName,APP_FILE_NAME_LEN);
			temp_up_file_info->file_size = fno.fsize - IMAGE_HEADER_LEN;
			temp_up_file_info->total_packet_count = (up_file_info[temp_file_count].file_size / APP_FILE_PACKET_LEN) + 
											        ((up_file_info[temp_file_count].file_size % APP_FILE_PACKET_LEN) ? 1 : 0);
			temp_up_file_info->cur_packet_index = 0;
			temp_up_file_info->file_header_len = IMAGE_HEADER_LEN;
			temp_file_count++;
			
			res = f_open(&file, fileName, FA_OPEN_EXISTING | FA_READ); // 路径+文件名
			if(res != FR_OK)
			{
				f_close(&file);
				DBG_Error("fopen file:%s err:%d",fno.fname,res);
				continue;
			}

			/* 读取文件头部信息 */
			res = f_read(&file, &image_info, IMAGE_HEADER_LEN, (void *)&numOfReadBytes);
			if((res != FR_OK) || (numOfReadBytes != IMAGE_HEADER_LEN))
			{
				f_close(&file);
				DBG_Error("app file read image info err");
				return res;
			}
			else
			{
				/* 提取文件信息 */				
				app_file->count = temp_file_count;
				app_file->ih_file = &image_info;
				app_file->count = temp_file_count;
				app_file->up_file_info[0] = &up_file_info[0];
				app_file->is_at32_up = 1;

			}
		}
		else
		{
			continue;
		}
		
	 f_close(&file);
	}
	DBG_log("app file count:%d",app_file->count);
	return res;
}
/**
***********************************************************
* @brief 	快速移动文件指针
* @param 
* @return 	 0=正确；1=错误
***********************************************************
*/
static FRESULT app_file_fast_seek(FIL* fp)
{
	FRESULT res;
	//以下代码实现快速移动文件指针(实践证实：读SD卡时必须开启快速移动文件指针，但读U盘时不能开启，否则连续读取时会造成中途读取失败)
	fp->cltbl = clmt;
	clmt[0] = SZ_TBL;
	res = f_lseek(fp, CREATE_LINKMAP);
	if (res != FR_OK) return res;
	//以上代码实现快速移动文件指针
	
	return FR_OK;
}

/**
***********************************************************
* @brief	读取文件指定位置的数据
* @param 
* @return 	0=正确；1=错误
***********************************************************
*/
static FRESULT app_file_seek_read(
    FIL* fp, 	    /* Pointer to the file object */
	FSIZE_t ofs,    /* File pointer from top of file */
	void* buff,	    /* Pointer to data buffer */
	UINT btr,	    /* Number of bytes to read */
	UINT* br	    /* Pointer to number of bytes read */)
{
    FRESULT res;
  	res = f_lseek(fp, ofs);

  	if(res == FR_OK) {
		res = f_read(fp, buff, btr, br);
	}

    return res;
}

/**
***********************************************************
* @brief	读取升级文件 
* @param 
* @return 
***********************************************************
*/
uint8_t app_file_read(app_file_t *app_file, uint8_t *pbuff)
{
	FRESULT res;
    static uint8_t is_load_file = 0;	// 是否已经载入升级文件
	uint32_t len, addr, numOfReadBytes;
	up_file_info_t *info;

	info = app_file->up_file_info[0];
	/* 加载升级文件 */
	if(!is_load_file)
	{
		/* 关闭已打开文件 */
		f_close(&file);

		/* 打开升级文件 */
		res = f_open(&file, (TCHAR *)info->file_name, FA_OPEN_EXISTING | FA_READ);
		if(res != FR_OK)
		{
			f_close(&file);
			return res;
		}
		
		/* 打开快速移动文件指针 */
		res = app_file_fast_seek(&file);
		if(res != FR_OK)
		{
			f_close(&file);
			return res;
		}
		is_load_file = 1;
	}

	
	/* 根据当前包数计算需要读取数据长度以及偏移地址 */
	len = (info->cur_packet_index == (info->total_packet_count -1)) ? 
		  (info->file_size % APP_FILE_PACKET_LEN) : APP_FILE_PACKET_LEN; 
	addr = info->file_header_len +  info->cur_packet_index * APP_FILE_PACKET_LEN;// 计算偏移地址
	res  = app_file_seek_read(&file, addr, pbuff, len, (void*)&numOfReadBytes); // 读取文件数据
	/* 判断读取是否正确 */
	if((res != FR_OK) || (numOfReadBytes != len)) { 
		f_close(&file);
		return res;
	}
	else 
	{
		
	}
    return res;	// 读取文件没有错误返回值
}
